[DNM][CORE-15273] kafka/server: client quota perf optmization#29506
[DNM][CORE-15273] kafka/server: client quota perf optmization#29506IoannisRP wants to merge 8 commits intoredpanda-data:devfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements performance optimizations for client quota operations by adding a rule counter mechanism and refactoring the quota lookup logic.
Changes:
- Moved
client_quota_ruleenum to a shared location (cluster::client_quota::rule) to enable reuse across components - Added rule counters to the quota store to track active quota rules and skip checking inactive ones
- Simplified benchmark tests by removing unused quota configuration parameters
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/v/kafka/server/tests/quota_manager_bench.cc | Removed unused fetch_tp field and associated benchmark test cases |
| src/v/kafka/server/quota_manager.cc | Updated references to use cluster::client_quota::all_client_quota_rules |
| src/v/kafka/server/client_quota_translator.h | Replaced local client_quota_rule definition with alias to cluster::client_quota::rule |
| src/v/kafka/server/client_quota_translator.cc | Refactored quota lookup to use rule counters for optimization and added helper functions |
| src/v/cluster/client_quota_types.h | New file defining shared rule enum and supporting constants |
| src/v/cluster/client_quota_types.cc | New file implementing stream operator for rule enum |
| src/v/cluster/client_quota_store.h | Added rule counters array and prefix group filter overloads |
| src/v/cluster/client_quota_store.cc | Implemented rule counter updates on quota set/remove/clear operations |
| src/v/cluster/BUILD | Added new client_quota_types source and header files |
| return os << "kafka_user_client_prefix"; | ||
| case rule::kafka_user_client_id: | ||
| return os << "kafka_user_client_id"; | ||
| } |
There was a problem hiding this comment.
Switch statement is missing a default case or return statement at the end, which can lead to undefined behavior if an invalid enum value is passed.
| } | |
| } | |
| return os << "unknown"; |
| rule get_rule(const entity_key& key) { | ||
| const bool has_user = has_part<entity_key::part::user_match>(key); | ||
| const bool has_user_default | ||
| = has_part<entity_key::part::user_default_match>(key); | ||
| const bool has_client_id = has_part<entity_key::part::client_id_match>(key); | ||
| const bool has_client_prefix | ||
| = has_part<entity_key::part::client_id_prefix_match>(key); | ||
| const bool has_client_default | ||
| = has_part<entity_key::part::client_id_default_match>(key); | ||
|
|
||
| if (has_user) { | ||
| if (has_client_id) { | ||
| return rule::kafka_user_client_id; | ||
| } | ||
| if (has_client_prefix) { | ||
| return rule::kafka_user_client_prefix; | ||
| } | ||
| if (has_client_default) { | ||
| return rule::kafka_user_client_default; | ||
| } | ||
| return rule::kafka_user; | ||
| } | ||
|
|
||
| if (has_user_default) { | ||
| if (has_client_id) { | ||
| return rule::kafka_user_default_client_id; | ||
| } | ||
| if (has_client_prefix) { | ||
| return rule::kafka_user_default_client_prefix; | ||
| } | ||
| if (has_client_default) { | ||
| return rule::kafka_user_default_client_default; | ||
| } | ||
| return rule::kafka_user_default; | ||
| } | ||
|
|
||
| if (has_client_id) { | ||
| return rule::kafka_client_id; | ||
| } | ||
| if (has_client_prefix) { | ||
| return rule::kafka_client_prefix; | ||
| } | ||
| if (has_client_default) { | ||
| return rule::kafka_client_default; | ||
| } | ||
|
|
||
| return rule::not_applicable; | ||
| } |
There was a problem hiding this comment.
The logic does not handle the case where both has_user and has_user_default are true, which could occur with malformed keys. Consider adding validation or asserting mutual exclusivity of these flags.
| case client_quota_rule::kafka_user_default_client_prefix: | ||
| case client_quota_rule::kafka_user_client_prefix: | ||
| vassert(false, "make_entity_key should not be called on these rules"); | ||
| } |
There was a problem hiding this comment.
Switch statement is missing a return or vassert at the end. If an unexpected enum value is passed, the function may return with undefined behavior.
| } | |
| } | |
| vassert( | |
| false, | |
| "Unknown client_quota_rule in make_entity_key: {}", | |
| static_cast<int>(r)); |
| case client_quota_rule::kafka_user_client_default: | ||
| case client_quota_rule::kafka_user_client_id: | ||
| vassert(false, "make_entity_key should not be called on these rules"); | ||
| } |
There was a problem hiding this comment.
Switch statement is missing a return or vassert at the end to handle potential undefined behavior if an unexpected enum value is passed.
| } | |
| } | |
| vassert(false, "Unexpected client_quota_rule in make_group_quotas: {}", | |
| r); |
| tracker_key make_tracker_key( | ||
| const std::string_view k1_name, const std::string_view k2_name) { | ||
| return tracker_key{ | ||
| std::in_place_type<std::pair<K1, K2>>, std::make_pair(k1_name, k2_name)}; | ||
| const client_quota_rule r, | ||
| std::string_view user, | ||
| // This is either the client name or the group name | ||
| std::string_view client_id) { | ||
| switch (r) { | ||
| case client_quota_rule::not_applicable: | ||
| return tracker_key{std::in_place_type<k_not_applicable>}; | ||
| case client_quota_rule::kafka_client_default: | ||
| return tracker_key{std::in_place_type<k_client_id>, client_id}; | ||
| case client_quota_rule::kafka_client_prefix: | ||
| return tracker_key{std::in_place_type<k_group_name>, client_id}; | ||
| case client_quota_rule::kafka_client_id: | ||
| return tracker_key{std::in_place_type<k_client_id>, client_id}; | ||
| case client_quota_rule::kafka_user_default: | ||
| return tracker_key{std::in_place_type<k_user>, user}; | ||
| case client_quota_rule::kafka_user_default_client_default: | ||
| return tracker_key{ | ||
| std::in_place_type<std::pair<k_user, k_client_id>>, | ||
| std::make_pair(user, client_id)}; | ||
| case client_quota_rule::kafka_user_default_client_prefix: | ||
| return tracker_key{ | ||
| std::in_place_type<std::pair<k_user, k_group_name>>, | ||
| std::make_pair(user, client_id)}; | ||
| case client_quota_rule::kafka_user_default_client_id: | ||
| return tracker_key{ | ||
| std::in_place_type<std::pair<k_user, k_client_id>>, | ||
| std::make_pair(user, client_id)}; | ||
| case client_quota_rule::kafka_user: | ||
| return tracker_key{std::in_place_type<k_user>, user}; | ||
| case client_quota_rule::kafka_user_client_default: | ||
| return tracker_key{ | ||
| std::in_place_type<std::pair<k_user, k_client_id>>, | ||
| std::make_pair(user, client_id)}; | ||
| case client_quota_rule::kafka_user_client_prefix: | ||
| return tracker_key{ | ||
| std::in_place_type<std::pair<k_user, k_group_name>>, | ||
| std::make_pair(user, client_id)}; | ||
| case client_quota_rule::kafka_user_client_id: | ||
| return tracker_key{ | ||
| std::in_place_type<std::pair<k_user, k_client_id>>, | ||
| std::make_pair(user, client_id)}; | ||
| } | ||
| } |
There was a problem hiding this comment.
Switch statement is missing a return or default case at the end. If an unexpected enum value is passed, the function may return with undefined behavior.
|
Results of
|
Retry command for Build#80033please wait until all jobs are finished before running the slash command |
2e1403b to
1dc3751
Compare
|
Given the complexity of the optimization, it was decided to take the perf-hit. |
Fixes: CORE-15273
introduces a tally for each rule present in client quota store. Only rules that have active entries are considered when trying to resolve an entity key.
Backports Required
Release Notes